package com.tiktok;



import com.motu.vertx.module.utility.base.BaseRedisKey;
import com.motu.vertx.module.utility.redis.RedisTools;
import com.motu.vertx.module.utility.toolset.TimeUtils;
import com.motu.vertx.module.utility.toolset.Tool;
import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Promise;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.client.HttpResponse;
import io.vertx.ext.web.client.WebClient;
import io.vertx.redis.client.RedisAPI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;


public class TiktokPayTool {
    private static final Logger logger = LoggerFactory.getLogger(TiktokPayTool.class);
    private static final String TiktokBaseUrl = "https://developer.toutiao.com";

    public static String getPaySign(JsonObject params,String uriMethod,String paykey){
        Map<String, String> paramsMap = new HashMap<String, String>();
        Iterator<Map.Entry<String, Object>> it = params.iterator();
        while (it.hasNext()) {
            Map.Entry<String, Object> entry = it.next();
            String key = entry.getKey();
            String value = String.valueOf(entry.getValue());
            if (value == null) {
                value = "";
            }
            paramsMap.put(key, value);
        }
        return getPaySign(paramsMap,uriMethod,paykey);
    }

    public static String getPaySign(Map<String, String> paramsMap,String uriMethod ,String paykey){
        //构建Sign签名
        String postData = Tool.getSignCheckContent(paramsMap, false);
        String signData = postData + uriMethod;
        String sign = Tool.sha256_HMAC(signData,paykey);
        logger.info("TiktokPayTool getsign: " + signData + " paykey: " + paykey + " sign: " + sign);
        return sign;
    }

    public static Future<String> getAccessToken(WebClient webClient, RedisAPI redisClient, String TIKTOK_GAME_APPID, String TIKTOK_GAME_LOGIN_KEY, String accessTokenHttp) {
        Promise<String> promise = Promise.promise();
        try {
            RedisTools.get(BaseRedisKey.getTiktokAccesstokenKey(), (tiktokAccesstoken) -> {
                if (tiktokAccesstoken.succeeded()) {
                    String accessToken = (String)tiktokAccesstoken.result();
                    if (Tool.isEmpty(accessToken)) {
                        Future<String> getTokenFuture = getAccessTokenByTiktokApi(webClient, redisClient, TIKTOK_GAME_APPID, TIKTOK_GAME_LOGIN_KEY, accessTokenHttp);
                        getTokenFuture.onComplete((tokenHandler) -> {
                            if (tokenHandler.succeeded()) {
                                String result = tokenHandler.result();
                                JsonObject jsonObject = new JsonObject(result);
                                if (jsonObject.getInteger("err_no") == 0) {
                                    String access_token = jsonObject.getString("access_token");
                                    promise.complete(access_token);
                                }
                            } else {
                                promise.fail(tokenHandler.cause());
                            }

                        });
                    } else {
                        promise.complete(accessToken);
                    }
                }

            });
        } catch (Exception e) {
            logger.error("#### PayTool getAccessToken Exception cause = " + Tool.getException(e));
            promise.fail(Tool.getException(e));
        }

        return promise.future();
    }


    /**
     * 从头条API的获取 抖音小程序的AccessToken
     * @return
     */
    public static Future<String> getAccessTokenByTiktokApi(WebClient webClient, RedisAPI redisClient,String TIKTOK_GAME_APPID,String TIKTOK_GAME_LOGIN_KEY,String accessTokenHttp) {
        Promise<String> promise = Promise.promise();
        try {
            if (!Tool.isEmpty(accessTokenHttp)) {
                //有配置获取地址,从获取地址获取 (TODO:评审服从正式服获取)
                String url = accessTokenHttp + "/tiktok/server/getAccessToken";
                webClient.getAbs(url).send(h -> {
                    if (h.succeeded()) {
                        HttpResponse<Buffer> response = h.result();
                        JsonObject resObj = new JsonObject(response.bodyAsString());
                        if (resObj.getInteger("err_no") == 0) {
                            //更新redis
                            String accessToken = resObj.getString("access_token");
                            int accessTokenTime = resObj.getInteger("access_token_time");
                            //access_token 的有效期目前为 2 个小时，需定时刷新，重复获取将导致上次获取的 access_token 失效；所以此处设置1小时50分钟的有效期
                            RedisTools.setAndExpire(BaseRedisKey.getTiktokAccesstokenKey(), accessToken, accessTokenTime);
                            promise.complete(accessToken);
                        } else {
                            logger.error("### getAccessTokenByTiktokApi SDK resObj:" + resObj);
                            promise.fail(resObj.toString());
                        }
                    } else {
                        logger.error("### getAccessTokenByTiktokApi HTTP resObj:" + h.cause());
                        promise.fail(h.cause());
                    }
                });
            } else {
                //从头条api获取
                String appid = TIKTOK_GAME_APPID;
                String appsecret = TIKTOK_GAME_LOGIN_KEY;
                String url = "https://minigame.zijieapi.com/mgplatform/api/apps/v2/token";
                JsonObject urlData = new JsonObject();
                urlData.put("grant_type","client_credential");
                urlData.put("appid",appid);
                urlData.put("secret",appsecret);

                webClient.postAbs(url).putHeader("content-type", "application/json").timeout(10000l).sendJsonObject(urlData,h -> {
                    if (h.succeeded()) {
                        HttpResponse<Buffer> response = h.result();
                        JsonObject resObj = response.bodyAsJsonObject();
                        //{"err_no":0,"err_tips":"success","data":{"access_token":"08011218462b72656c394a5774692f4c653776694d726d7236673d3d","expires_in":7200,"expiresAt":1723441751,"expires_at":1723441751}}

                        int err_no = resObj.getInteger("err_no");
                        if (err_no != 0) {
                            //返回的错误格式 {"errcode":40013,"errmsg":"invalid appid"}
                            logger.error("### getAccessTokenByTiktokApi SDK resObj:" + resObj);
                            promise.fail(resObj.toString());
                        } else {
                            //更新redis
                            JsonObject tokenData = resObj.getJsonObject("data");
                            String accessToken = tokenData.getString("access_token");
                            int expires_at = tokenData.getInteger("expires_at");
                            int expires_in = tokenData.getInteger("expires_in");

                            //access_token 的有效期目前为 2 个小时，需定时刷新，重复获取将导致上次获取的 access_token 失效；所以此处设置1小时50分钟的有效期
                            RedisTools.setAndExpire(BaseRedisKey.getTiktokAccesstokenKey(), accessToken, expires_in - 10 * Tool.MINUTE);
                            promise.complete(accessToken);
                        }
                    } else {
                        logger.error("### getAccessTokenByTiktokApi HTTP resObj:" + h.cause());
                        promise.fail(h.cause());
                    }
                });
            }
        } catch (Exception e) {
            logger.error("#### getAccessTokenByTiktokApi Exception cause = " + Tool.getException(e));
            promise.fail(Tool.getException(e));
        }
        return promise.future();
    }

    /**
     * 抖音小游戏 查询游戏币余额
     * @param webClient
     * @param payKey
     * @param access_token
     * @param appid
     * @param pf
     * @param openId
     * @param zone_id
     * @return
     */
    public static Future<JsonObject> checkTiktokBalance(WebClient webClient,String payKey,String access_token,String appid, String pf , String openId, String zone_id) {
        Promise<JsonObject> promise = Promise.promise();

        try {
            long curTime = TimeUtils.getCurTime();
            JsonObject data = new JsonObject();
            data.put("openid", openId);//用户唯一标识
            data.put("appid", appid);//小程序唯一标识
//            data.put("offer_id", offer_id);//非必填-扩展字段，目前未使用，可不填
            data.put("ts", curTime);//UNIX 时间戳，单位秒
            data.put("zone_id", zone_id);//游戏服务区 id，开发者自定义。游戏不分大区则默认填写"1"。如果应用支持多角色，则角色 ID 接在分区 ID 后，用"_"连接 TODO:zone_id = 1001_100000000001
            data.put("pf", pf);//平台 目前仅为安卓："android", "ios"
//            data.put("user_ip", offer_id);//用户外网 ip-非必填
//            data.put("sig", sig);//扩展字段，目前未使用，可不填
            data.put("access_token", access_token);

            String uri = "/api/apps/game/wallet/get_balance";
            String getBalanceUrl = TiktokBaseUrl + uri;
            String uriMethod = "&org_loc="+uri+"&method=POST";
            String mp_sig = getPaySign(data,uriMethod,payKey);
            data.put("mp_sig", mp_sig);//支付签名 , 本次请求的所有非空参数+uri+method+支付密钥，用 HMAC-SHA256 签名

            webClient.postAbs(getBalanceUrl).sendJsonObject(data, (h) -> {
                if (h.succeeded()) {
                    HttpResponse<Buffer> response = (HttpResponse)h.result();
                    JsonObject resObj = response.bodyAsJsonObject();
                    promise.complete(resObj);
                } else {
                    logger.error("#### checkTiktokBalance cause = " + h.cause());
                    JsonObject error = new JsonObject();
                    error.put("errcode", 101);
                    error.put("errmsg", h.cause());
                    promise.fail(error.toString());
                }

            });
        } catch (Exception e) {
            logger.error("#### checkTiktokBalance Exception cause = " + Tool.getException(e));
            JsonObject error = new JsonObject();
            error.put("errcode", 101);
            error.put("errmsg", Tool.getException(e));
            promise.fail(error.toString());
        }

        return promise.future();
    }

    /**
     * 抖音小游戏 扣除游戏币操作
     * @param webClient
     * @param payKey
     * @param pf
     * @param openId
     * @param zone_id
     * @param amt
     * @param bill_no
     * @param pay_item
     * @param handler
     */
    public static void payTiktokBalance(WebClient webClient,RedisAPI redisClient,JsonObject tokenParams,String payKey, String pf , String openId, String zone_id,int amt,String bill_no,String pay_item, Handler<AsyncResult<String>> handler){
        try {
            String TIKTOK_GAME_APPID = tokenParams.getString("TIKTOK_GAME_APPID");
            String TIKTOK_GAME_LOGIN_KEY = tokenParams.getString("TIKTOK_GAME_LOGIN_KEY");
            String TIKTOK_MINIGAME_ACCESSTOKEN_HTTP = tokenParams.getString("TIKTOK_MINIGAME_ACCESSTOKEN_HTTP");
            Future<String> tokenFuture = getAccessToken(webClient, redisClient, TIKTOK_GAME_APPID, TIKTOK_GAME_LOGIN_KEY, TIKTOK_MINIGAME_ACCESSTOKEN_HTTP);
            tokenFuture.onComplete((tokenHandler) -> {
                if (tokenHandler.succeeded()) {
                    String accessToken = tokenHandler.result();
                    if (!Tool.isEmpty(accessToken)) {
                        long curTime = TimeUtils.getCurTime();
                        JsonObject data = new JsonObject();
                        data.put("openid", openId);//用户唯一标识
                        data.put("appid", TIKTOK_GAME_APPID);//小程序唯一标识
//                        data.put("offer_id", offer_id);//非必填-扩展字段，目前未使用，可不填
                        data.put("ts", curTime);//UNIX 时间戳，单位秒
                        data.put("zone_id", zone_id);//游戏服务区 id，开发者自定义。游戏不分大区则默认填写"1"。如果应用支持多角色，则角色 ID 接在分区 ID 后，用"_"连接 TODO:zone_id = 1001_100000000001
                        data.put("pf", pf);//平台 目前仅为安卓："android", "ios"
//                        data.put("user_ip", offer_id);//用户外网 ip-非必填
//                        data.put("sig", sig);//扩展字段，目前未使用，可不填
                        data.put("amt", amt);//扣除游戏币数量，不能为 0
                        data.put("bill_no", bill_no);//扣游戏币的开发者订单号，开发者需要保证全局唯一。相同的订单号不会重复扣款。长度不超过 63，只能是数字、大小写字母_-
                        data.put("pay_item", pay_item);//游戏道具名，开发者自定义称
//                      data.put("app_remark", app_remark);//游戏开发者备注，会写到账户流水。开发者自定义
                        data.put("access_token", accessToken);

                        String uri = "/api/apps/game/wallet/game_pay";
                        String getPayUrl = TiktokBaseUrl + uri;
                        String uriMethod = "&org_loc="+uri+"&method=POST";
                        String mp_sig = getPaySign(data,uriMethod,payKey);
                        data.put("mp_sig", mp_sig);//支付签名 , 本次请求的所有非空参数+uri+method+支付密钥，用 HMAC-SHA256 签名
                        logger.info("#### payTiktokBalance data = " + data);
                        webClient.postAbs(getPayUrl).sendJsonObject(data, (h) -> {
                            if (h.succeeded()) {
                                HttpResponse<Buffer> response = (HttpResponse)h.result();
                                JsonObject resObj = response.bodyAsJsonObject();
                                if (resObj.getInteger("errcode") == 0) {
                                    handler.handle(Future.succeededFuture("success"));
                                }else {
                                    logger.error("#### payTiktokBalance is failed!!! resObj = " + resObj + " data:" + data);
                                    handler.handle(Future.failedFuture(resObj.toString()));
                                }
                            } else {
                                logger.error("#### payTiktokBalance cause = " + h.cause());
                                JsonObject error = new JsonObject();
                                error.put("errcode", 101);
                                error.put("errmsg", h.cause());
                                handler.handle(Future.failedFuture(error.toString()));
                            }
                        });
                    }else {
                        logger.error("#### payTiktokBalance accessToken is empty!!! tokenParams:" + tokenParams + " payKey:" + payKey + " pf:" + pf + " openId:" + openId + " zone_id:" + zone_id + " amt:" + amt + " bill_no:" + bill_no + " pay_item:" + pay_item );
                        JsonObject error = new JsonObject();
                        error.put("errcode", 102);
                        error.put("errmsg", "payTiktokBalance accessToken is empty!!!");
                        handler.handle(Future.failedFuture(error.toString()));
                    }
                }
            });

        } catch (Exception e) {
            logger.error("#### payTiktokBalance Exception cause = " + Tool.getException(e));
            JsonObject error = new JsonObject();
            error.put("errcode",101);
            error.put("errmsg",Tool.getException(e));
            handler.handle(Future.failedFuture(error.toString()));
        }
    }

    /**
     * 抖音小游戏 订阅推送
     * @param webClient
     * @param redisClient
     * @param tokenParams
     * @param openId
     * @param tpl_id
     * @param msgData
     */
    public static Future<JsonObject> pushTiktokSubscribe(WebClient webClient,RedisAPI redisClient,JsonObject tokenParams, String openId,String tpl_id,Map<String ,String> msgData){
        Promise<JsonObject> promise = Promise.promise();
        try {
            String TIKTOK_GAME_APPID = tokenParams.getString("TIKTOK_GAME_APPID");
            String TIKTOK_GAME_LOGIN_KEY = tokenParams.getString("TIKTOK_GAME_LOGIN_KEY");
            String TIKTOK_MINIGAME_ACCESSTOKEN_HTTP = tokenParams.getString("TIKTOK_MINIGAME_ACCESSTOKEN_HTTP");
            Future<String> tokenFuture = getAccessToken(webClient, redisClient, TIKTOK_GAME_APPID, TIKTOK_GAME_LOGIN_KEY, TIKTOK_MINIGAME_ACCESSTOKEN_HTTP);
            tokenFuture.onComplete((tokenHandler) -> {
                if (tokenHandler.succeeded()) {
                    String accessToken = tokenHandler.result();
                    if (!Tool.isEmpty(accessToken)) {
                        JsonObject data = new JsonObject();
                        data.put("access_token", accessToken);
                        data.put("open_id", openId);//用户唯一标识
                        data.put("app_id", TIKTOK_GAME_APPID);//小程序唯一标识
                        data.put("tpl_id", tpl_id);//模板的 id
                        data.put("data", msgData);//填充模板的MAP

                        String uri = "https://minigame.zijieapi.com/mgplatform/api/apps/subscribe_notification/developer/v1/notify";
                        logger.info("#### pushTiktokSubscribe data = " + data);
                        webClient.postAbs(uri).sendJsonObject(data, (h) -> {
                            if (h.succeeded()) {
                                HttpResponse<Buffer> response = (HttpResponse)h.result();
                                JsonObject resObj = response.bodyAsJsonObject();
                                if (resObj.getInteger("err_no") == 0) {
                                    promise.complete(resObj);
                                }else {
                                    logger.error("#### pushTiktokSubscribe is failed!!! resObj = " + resObj + " data:" + data);
                                    promise.fail(" sendWxSubscribe errro : " +resObj.toString());

                                }
                            } else {
                                logger.error("#### pushTiktokSubscribe cause = " + h.cause());
                                JsonObject error = new JsonObject();
                                error.put("errcode", 101);
                                error.put("errmsg", h.cause());
                                promise.fail(" sendWxSubscribe errro : " +error.toString());
                            }
                        });
                    }else {
                        logger.error("#### pushTiktokSubscribe accessToken is empty!!! tokenParams:" + tokenParams +  " openId:" + openId + " tpl_id:" + tpl_id + " msgData:" + msgData );
                        JsonObject error = new JsonObject();
                        error.put("errcode", 102);
                        error.put("errmsg", "pushTiktokSubscribe accessToken is empty!!!");
                        promise.fail(" sendWxSubscribe errro : " +error.toString());
                    }
                }
            });

        } catch (Exception e) {
            logger.error("#### pushTiktokSubscribe Exception cause = " + Tool.getException(e));
            JsonObject error = new JsonObject();
            error.put("errcode",101);
            error.put("errmsg",Tool.getException(e));
            promise.fail(" sendWxSubscribe errro : " +error.toString());
        }
        return promise.future();
    }
}
